oo::class create Complete {
    superclass Plugin
    variable Language RenamedPadWidget SearchWidgets Limit StartLen \
        From Choices SearchState

    constructor {renamedPadWidget language startLen} {
        set Language $language
        set RenamedPadWidget $renamedPadWidget
        lappend SearchWidgets $RenamedPadWidget
        set StartLen $startLen
        set From {}
        set Choices {}
        set SearchState $RenamedPadWidget-backward

	set PadWidget [string trimleft $RenamedPadWidget _]
        bind $PadWidget <Control-quoteleft> [subst {
	    set comObj [self object]
            \$comObj completeWord \[$RenamedPadWidget index insert\] \[\$comObj getChoiceManually \[$RenamedPadWidget index insert\]\]
        }]
	bind $PadWidget <Escape> [subst {+
	    set ids \[$RenamedPadWidget tag ranges Complete\]
	    if {\$ids != {}} {
		$PadWidget mark set insert \[lindex \$ids end\]
	    }
	}]
    }

    method appendSearchWidget {w} {
        lappend SearchWidgets $w
    }

    method isInCompleteMode? {} {
        return [expr [llength [$RenamedPadWidget tag range Complete]] != 0]
    }
    
    method clearCompleteMode {args} {
        $RenamedPadWidget tag remove Complete 1.0 end
        set From {}
        set Choices {}
	set SearchState $RenamedPadWidget-backward
    }

    method findCompleteChoice {index widget from {dir forward}} {
	# puts "$index $widget $from $dir"
	if [my isInCompleteMode?] {
	    # puts RUNTOHERE
            set index [lindex [$RenamedPadWidget tag range Complete] 0]
	}

	set prefix [$RenamedPadWidget get [$Language getWordHeadIndex $RenamedPadWidget $index] $index]
        if {$dir == {backward}} {
            set lineStart [$widget index "$from linestart"]
            set content [$widget get $lineStart $from]
        } else {
            set lineStart [$Language getWordEndIndex $widget $from]
            set content [$widget get $lineStart "$from lineend"]
        }

        while {![info exists preStart] || int($preStart) != int($lineStart)} {
            foreach w [regexp -inline -all {[\w:]+} $content] {
                if {![string first $prefix $w] && $w != $prefix} {
                    # SET STATE: FROM
                    if {[lsearch $Choices $w] == -1} {
                        set From [$widget index $lineStart+[string first $w $content]c]
                        lappend Choices $w
                        return $w
                    }
                }
            }
            set preStart $lineStart
            if {$dir == {backward}} {
                set lineStart [$RenamedPadWidget index "$lineStart -1l linestart"]
            } else {
                set lineStart [$RenamedPadWidget index "$lineStart +1l linestart"]
            }
            set content [$RenamedPadWidget get $lineStart "$lineStart lineend"]
        }
    }

    method findNextChoice {index} {
        foreach {widget dir} [split $SearchState -] {}
        set choice [my findCompleteChoice $index $widget $From $dir]
        if {$choice == {}} {
            # SET STATES
            if {$dir == "backward"} {
                set SearchState $widget-forward
            } else {
                set id [lsearch $SearchWidgets $widget]
                if {$id == [llength $SearchWidgets]-1} {
                    return
                }
                set nextWidget [lindex $SearchWidgets $id+1]
                set SearchState $nextWidget-forward
                set From 1.0
            }
            tailcall my findNextChoice $index
        } else {
            return $choice
        }
    }

    method completeWord {index word} {
        set len [string length \
                     [$RenamedPadWidget get [$Language getWordHeadIndex $RenamedPadWidget $index] $index]]
        set completePart [string range $word $len end]
	set wordEnd [$Language getWordEndIndex $RenamedPadWidget $index]
	# puts "[$RenamedPadWidget index $index], $len, $completePart, $wordEnd"
        [string trimleft $RenamedPadWidget _] replace $index $wordEnd $completePart
	$RenamedPadWidget tag add Complete $index $index+[string length $completePart]c
	$RenamedPadWidget mark set insert $index
    }

    method getChoiceAfterInsert {index content args} {
	set i 0; set ids [$RenamedPadWidget tag range Complete]
        while {$i < [llength $ids]-1} {
            $RenamedPadWidget delete [lindex $ids $i] [lindex $ids $i+1]
            incr i 2
        }

	set index [$RenamedPadWidget index $index+[string length $content]c]
        set worStart [$Language getWordHeadIndex $RenamedPadWidget $index]
        set prefix [$RenamedPadWidget get $worStart $index]
        if {[string length $prefix] < $StartLen} { return }
        
        my clearCompleteMode
        set From $worStart
        if {[set choice [my findNextChoice $index]] != {}} {
            my completeWord $index $choice
	    # $RenamedPadWidget mark set insert $index
        }
    }

    method getChoiceManually {index} {
	if {$From == {}} {set From [$RenamedPadWidget index insert]}
	# puts $Choices
        set choice [my findNextChoice $index]
        if {$choice == {}} {
            set choice [lindex $Choices 0]
            set Choices [lreplace $Choices 0 0]
            lappend Choices $choice
        }
        return $choice
    }

    method getPluginEvents {} {
        return {
            {afterInsert getChoiceAfterInsert}
	    {afterMarkSetInsert clearCompleteMode}
        }
    }
}